package main

import (
	"fmt"
	"time"
)

/*
* Content: groutine-channel、遍历channel、select case多路复用、channel超时机制 (控制 Groutine 退出)
* --------------------------------------------------
* 什么时候用Goroutine？什么时候用Channel？: https://juejin.cn/post/6943952470993272845
 */

func MyGroutineChannel() {

	// groutine-channel
	fmt.Println("--------------------groutine-channel--------------------")

	ch1 := make(chan int)
	ch2 := make(chan int)

	// 开启 goroutine 把 0-100 写入到 ch1 通道中
	go func() {
		for i := 0; i < 8; i++ {
			ch1 <- i
		}
		close(ch1) // 记得关闭 channel
	}() // 后面要加个括号

	// 开启 goroutine 从ch1中取值，值的平方赋值给 ch2
	go func() {
		//for i := 0; i < 8; i++ {   // 这种方式不够优雅与灵活
		for {
			i, ok := <-ch1 // 通道关闭时 for range 会自动退出
			if ok {
				ch2 <- i * i
			} else {
				break
			}
		}
		close(ch2)
	}()

	// 遍历channel
	fmt.Println("--------------------遍历channel--------------------")

	// 主 goroutine 从 ch2 中取值 打印输出
	// for x := chan 有值取值, 通道关闭时跳出goroutine
	for i := range ch2 {
		fmt.Println(i)
	}

	// 睡眠一会, 不然执行太快, 协程读写的效果看不到
	time.Sleep(time.Second * 2)

	// 只读通道 和 只写通道
	fmt.Println("--------------------只读通道和只写通道--------------------")
	// 通道作为参数: 限制该函数中可以对此 channel 的操作: http://c.biancheng.net/view/99.html

	// select case多路复用
	fmt.Println("--------------------select case多路复用--------------------")

	// 可处理一个或多个channel的发送/接收操作。
	ch5 := make(chan int, 1)
	go func() {
		fmt.Println("启动 [select case多路复用] 协程")
		for i := 0; i < 10; i++ {
			select {
			case x := <-ch5:
				fmt.Println(x)
			case ch5 <- i:
			}
		}
	}()

	// 睡眠一会, 不然执行太快, 协程读写的效果看不到
	time.Sleep(time.Second * 2)

	// channel超时机制
	fmt.Println("--------------------channel超时机制--------------------")

	// 无缓冲 channel
	ch8 := make(chan int)
	quit := make(chan bool) // 用于控制超时, 先阻塞程序运行, 协程阻塞时间到了之后, 会往此 channel 内写入数据, 结束阻塞
	go func() {
		for { // 无线 for 循环
			select {
			case num := <-ch8:
				fmt.Println("Num =", num)
			case <-time.After(time.Second * 3):
				fmt.Println("等待3秒仍未接收到数据, 退出等待！")
				quit <- true // 写入
			}

		}
	}() //别忘了()

	for i := 0; i < 5; i++ {
		ch8 <- i
		time.Sleep(time.Second)
	}

	<-quit // 这里暂时阻塞，直到可读。非常经典的应用
	fmt.Println("程序执行结束！")

}

// 通道作为参数: 限制该函数中可以对此 channel 的操作
func input(in chan<- int) { // 只写通道
	defer close(in)
	for i := 0; i < 100; i++ {
		in <- i
	}
}
